﻿using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using Raven.Abstractions.Indexing;
using Raven.Client.Document;
using Raven.Client.Indexes;
using Raven.Client.Linq;

namespace Org.Flowertwig.Media.Information.Services
{
    public class Storage
    {
        private static DocumentStore _docStore;
        public static DocumentStore DocumentStore
        {
            get
            {
                if (_docStore == null)
                {
                    // http://blogs.msdn.com/b/jnak/archive/2010/07/22/verificationexception-from-windows-azure-intellitrace.aspx
                    _docStore = new DocumentStore
                                    {
                                        Url = Properties.Settings.Default.RavenDbServerUrl,
                                        DefaultDatabase = Properties.Settings.Default.RavenDBDatabaseName
                                    };
                    _docStore.Initialize();
                }
                return _docStore;
            }
            set
            {
                if (_docStore == null)
                    return;

                _docStore = value;
                _docStore.Initialize();
            }
        }

        //public static void EnsureIndexes()
        //{
        //    try
        //    {
        //        var index = DocumentStore.DatabaseCommands.GetIndex("IndexesVersion");

        //        // TODO: Add creation of all indexes needed here.

        //    }
        //    catch (Exception ex)
        //    {
        //        //ex.ToString();
        //    }

        //}

        public static void AddService(Service service)
        {
            try
            {
                using (var session = DocumentStore.OpenSession())
                {
                    var cloned = service.Clone();
                    cloned.Id = "Services/" + service.Id;

                    session.Store(cloned);

                    session.SaveChanges();
                }
            }
            catch (Exception ex)
            {
                //ex.ToString();
            }
        }

        public static void AddSerie(Serie serie)
        {
            try
            {
                using (var session = DocumentStore.OpenSession())
                {
                    session.Store(serie);

                    session.SaveChanges();
                }
            }
            catch (Exception ex)
            {
                //ex.ToString();
            }
        }

        public static Cache GetCache(string id)
        {
            try
            {
                using (var session = DocumentStore.OpenSession())
                {
                    var cache = session.Load<Cache>(id);
                    return cache;
                }
            }
            catch (Exception ex)
            {
                //ex.ToString();
            }
            return null;
        }

        public static void AddCache(Cache cache)
        {
            try
            {
                using (var session = DocumentStore.OpenSession())
                {
                    session.Store(cache);

                    session.SaveChanges();
                }
            }
            catch (Exception ex)
            {
                //ex.ToString();
            }
        }

        public static void AddMovieInformation()
        {

        }

        public static void AddOtherInformation()
        {

        }

        public static IEnumerable<T> GetDocumentsForIndex<T>(string indexName)
        {
            return GetDocumentsForIndex<T>(indexName, 0, 128);
        }

        //public static IEnumerable<T> GetDocumentsForIndex<T>(string indexName, int startIndex, int length)
        //{
        //    try
        //    {
        //        using (var session = DocumentStore.OpenSession())
        //        {
        //            var result = session.Query<T>().Where().Skip(startIndex).Take(length);
        //            //var result = session.Query<T>(indexName).Where().Skip(startIndex).Take(length);
        //            return result;
        //        }
        //    }
        //    catch (Exception exception)
        //    {
        //        // TODO: Add better exception handling here ;)
        //    }
        //    return Enumerable.Empty<T>();
        //}

        public static IEnumerable<T> GetDocumentsForIndex<T>(string indexName, int startIndex, int length)
        {
            try
            {
                using (var session = DocumentStore.OpenSession())
                {
                    var result = Queryable.Skip(session.Query<T>(indexName), startIndex).Take(length);
                    return result;
                }
            }
            catch (Exception exception)
            {
                // TODO: Add better exception handling here ;)
            }
            return Enumerable.Empty<T>();
        }

        public static IEnumerable<T> GetDocuments<T>(string[] ids)
        {
            try
            {
                using (var session = DocumentStore.OpenSession())
                {
                    var result = session.Load<T>(ids);
                    return result;
                }
            }
            catch (Exception exception)
            {
                // TODO: Add better exception handling here ;)
            }
            return Enumerable.Empty<T>();
        }

        public static T GetDocument<T>(string id)
        {
            try
            {
                using (var session = DocumentStore.OpenSession())
                {
                    var result = session.Load<T>(id);
                    return result;
                }
            }
            catch (Exception exception)
            {
                // TODO: Add better exception handling here ;)
            }
            return default(T);
        }

        public static string GetDocumentUrl<T>(object obj)
        {
            try
            {
                using (var session = DocumentStore.OpenSession())
                {
                    var result = session.Advanced.GetDocumentUrl(obj);
                    return result;
                }
            }
            catch (Exception exception)
            {
                // TODO: Add better exception handling here ;)
            }
            return string.Empty;
        }

        public static string GetDocumentId<T>(object obj)
        {
            try
            {
                using (var session = DocumentStore.OpenSession())
                {
                    var result = session.Advanced.GetDocumentId(obj);
                    return result;
                }
            }
            catch (Exception exception)
            {
                // TODO: Add better exception handling here ;)
            }
            return string.Empty;
        }

        public static bool HasIndex(string indexName)
        {
            try
            {
                return DocumentStore.DatabaseCommands.GetIndex(indexName) != null;
            }
            catch (Exception exception)
            {
                // TODO: Add better exception handling here ;)
            }
            return false;
        }

        public static void EnsureIndex<TSource,TResult>(string indexName, IndexDefinitionBuilder<TSource,TResult> indexDefinitionBuilder)
        {
            try
            {
                if (DocumentStore.DatabaseCommands.GetIndex(indexName) == null)
                {
                    DocumentStore.DatabaseCommands.PutIndex(indexName, indexDefinitionBuilder);
                }
            }
            catch (Exception exception)
            {
                // TODO: Add better exception handling here ;)
            }
        }

        public static void EnsureIndex<T>(string indexName, IndexDefinitionBuilder<T> indexDefinitionBuilder)
        {
            try
            {
                if (DocumentStore.DatabaseCommands.GetIndex(indexName) == null)
                {
                    DocumentStore.DatabaseCommands.PutIndex(indexName, indexDefinitionBuilder);
                }
            }
            catch (Exception exception)
            {
                // TODO: Add better exception handling here ;)
            }
        }

        public static void AddIndex<T>(string indexName, IndexDefinitionBuilder<T> indexDefinitionBuilder)
        {
            try
            {
                DocumentStore.DatabaseCommands.PutIndex(indexName, indexDefinitionBuilder);
            }
            catch (Exception exception)
            {
                // TODO: Add better exception handling here ;)
            }
        }

        public static void AddIndex<T1,T2>(string indexName, IndexDefinitionBuilder<T1,T2> indexDefinitionBuilder)
        {
            try
            {
                DocumentStore.DatabaseCommands.PutIndex(indexName, indexDefinitionBuilder);
            }
            catch (Exception exception)
            {
                // TODO: Add better exception handling here ;)
            }
        }

        public static void DeleteAllItemsOfType<T>(string indexName)
        {
            try
            {
                using (var session = DocumentStore.OpenSession())
                {
                    var result = session.Query<T>(indexName);
                    RavenQueryStatistics statistics;
                    result.Statistics(out statistics);

                    var totalCount = statistics.TotalResults;
                    int i = 0;
                    const int deleteSpan = 1000;
                    do
                    {
                        var items = result.Skip(i).Take(deleteSpan);
                        foreach (T tObj in items)
                        {
                            session.Delete(tObj);
                        }
                        session.SaveChanges();

                        i += deleteSpan;
                    } while (i < totalCount);
                }
            }
            catch (Exception exception)
            {
                // TODO: Add better exception handling here ;)
            }
        }


        public static void AddIndex<T>(string indexName, IndexDefinition indexDefinition)
        {
            try
            {
                DocumentStore.DatabaseCommands.PutIndex(indexName, indexDefinition);
            }
            catch (Exception exception)
            {
                // TODO: Add better exception handling here ;)
            }
        }

        public static void DeleteIndex(string indexName)
        {
            try
            {
                DocumentStore.DatabaseCommands.DeleteIndex(indexName);
            }
            catch (Exception exception)
            {
                // TODO: Add better exception handling here ;)
            }
        }

        public static void AddMedias(Service service, IList<Media> releases)
        {
            try
            {
                bool ignoreFolders = releases.Count > 1;

                using (var session = DocumentStore.OpenSession())
                {
                    var serviceId = "Services/" + service.Id;

                    foreach (var release in releases)
                    {
                        // make sure we have correct values for release
                        if (string.IsNullOrEmpty(release.ServiceId))
                            release.ServiceId = serviceId;

                        if (ignoreFolders)
                        {
                            int pos = release.Name.LastIndexOf(Path.DirectorySeparatorChar);
                            if (pos >= 0)
                            {
                                release.Name = release.Name.Substring(pos);
                            }
                        }
                        else
                        {
                            int pos = release.Name.LastIndexOf(Path.DirectorySeparatorChar);
                            if (pos >= 0)
                            {
                                pos--;
                                int pos2 = release.Name.LastIndexOf(Path.DirectorySeparatorChar, pos);
                                if (pos2 >= 0)
                                {
                                    pos2++;
                                    release.Name = release.Name.Substring(pos2);
                                }
                            }
                        }

                        if (string.IsNullOrEmpty(release.Id))
                        {
                            // Generate id from Name
                            release.Id = Hash.CreateId(release.Name);
                        }

                        release.Added = DateTime.Now;

                        // Ensure that we are not overriding a valid media post
                        var dbItem = session.Load<Media>(release.Id);
                        if (dbItem == null || dbItem.InformationId == null)
                        {
                            session.Store(release);
                        }
                    }

                    session.SaveChanges();
                }
            }
            catch (Exception ex)
            {
                //ex.ToString();
            }
        }

        public static void UpdateMedia(Media media)
        {
            try
            {
                using (var session = DocumentStore.OpenSession())
                {
                    media.Added = DateTime.Now;

                    session.Store(media);
                    session.SaveChanges();
                }
            }
            catch (Exception ex)
            {
                //ex.ToString();
            }
        }

        public static void AddPages(Object[] obj)
        {
            try
            {
                using (var session = DocumentStore.OpenSession())
                {
                    foreach (var o in obj)
                    {
                        session.Store(obj);
                    }

                    session.SaveChanges();
                }
            }
            catch (Exception ex)
            {
                //ex.ToString();
            }
        }

        public static void AddDocument(object obj)
        {
            try
            {
                using (var session = DocumentStore.OpenSession())
                {
                    session.Store(obj);

                    session.SaveChanges();
                }
            }
            catch (Exception ex)
            {
                //ex.ToString();
            }
        }
    }
}
